﻿using System;
using System.Collections;
using FT.Architecture.NHibernate.Factories;
using NHibernate;

namespace FT.Architecture.NHibernate.Context
{
    /// <summary>
    /// Handles creation and management of sessions and transactions.  It is a singleton because 
    /// building the initial session factory is very expensive. Inspiration for this class came 
    /// from Chapter 8 of Hibernate in Action by Bauer and King.  Although it is a sealed singleton
    /// you can use TypeMock (http://www.typemock.com) for more flexible testing.
    /// </summary>
    internal sealed class NHibernateSessionManager
    {
        private readonly INHibernateConfigurator _nHibernateConfigurator;
        private readonly static object _locker = new object();
        private readonly Hashtable _sessionFactories = new Hashtable();
        private static NHibernateSessionManager _instance;

        private NHibernateSessionManager(INHibernateConfigurator nHibernateConfigurator)
        {
            _nHibernateConfigurator = nHibernateConfigurator;
        }

        /// <summary>
        /// This method attempts to find a session factory stored in <see cref="_sessionFactories" />
        /// via its name; if it can't be found it creates a new one and adds it the hashtable.
        /// </summary>
        /// <param name="sessionFactoryConfigPath">Path location of the factory config</param>
        public ISessionFactory GetSessionFactoryFor(string sessionFactoryConfigPath)
        {
            lock (_locker)
            {
                // Attempt to retrieve a stored SessionFactory from the hashtable.
                var sessionFactory = (ISessionFactory) _sessionFactories[sessionFactoryConfigPath];

                // Failed to find a matching SessionFactory so make a new one.
                if (sessionFactory == null)
                {
                    var configuration = _nHibernateConfigurator.Configure();
                    sessionFactory = configuration.BuildSessionFactory();

                    if (sessionFactory == null)
                    {
                        throw new InvalidOperationException("cfg.BuildSessionFactory() returned null.");
                    }

                    _sessionFactories.Add(sessionFactoryConfigPath, sessionFactory);
                }

                return sessionFactory;
            }
        }

        public static NHibernateSessionManager GetInstance(INHibernateConfigurator configurator)
        {
            if (_instance != null)
                return _instance;

            lock (_locker)
            {
                if (_instance == null)
                {
                    _instance = new NHibernateSessionManager(configurator);
                }

                return _instance;
            }
        }
    }
}
